home *** CD-ROM | disk | FTP | other *** search
- /**
- GRAB Graph Layout and Browser System
-
- Copyright (c) 1987, 1988, 1989 Stanford University
- Copyright (c) 1989, Tera Computer Company
- **/
-
- /**
- Implementation of the graph area.
-
- Lots of neat trickiness in here. Most notably: force the graph
- to be redrawn when the window is resized. Stop the graph from being
- redrawn superfluously. Perspective fu. Mouse Button fu. Moving
- rectangles and line fu. Joe Bob says check it out.
- **/
-
- #include <InterViews/brush.h>
- #include <InterViews/canvas.h>
- #include <InterViews/event.h>
- #include <InterViews/interactor.h>
- #include <InterViews/painter.h>
- #include <InterViews/perspective.h>
- #include <InterViews/rubband.h>
- #include <InterViews/rubline.h>
- #include <InterViews/rubrect.h>
- #include <InterViews/scene.h>
- #include <InterViews/sensor.h>
- #include <InterViews/shape.h>
- #include <string.h>
- #include <math.h>
- #include "graph.h"
- #include "gview.h"
- #include "gframe.h"
- #include "routines.h"
- #include "curs.h"
-
- static const defaultwidth = 600;
- static int defaultheight = 400;
-
- GView::GView (Graph* g) : (allEvents, nil)
- {
- perspective = new Perspective();
- graph = g;
- g->SetGB(this); /* tell the graph who we are */
- frame = nil;
- currno = 0;
- okaytodraw = false;
- resizedone = false;
- firsttime = true;
- Init();
- }
-
- void GView::Init ()
- {
- shape->width = defaultwidth;
- shape->height = (int) ((double) shape->width / GetAspRatio());
- shape->hshrink = hfil;
- shape->hstretch = hfil;
- shape->vshrink = vfil;
- shape->vstretch = vfil;
- x0 = 0;
- y0 = 0;
- mag = 1.0;
- FixPerspective(perspective);
- }
-
- void GView::FixPerspective(Perspective* p)
- /* make sure the perspective has the proper values */
- {
- p->x0 = 0;
- p->y0 = 0;
-
- if (canvas != nil)
- {
- p->curwidth = canvas->Width();
- p->curheight = canvas->Height();
- }
- else
- {
- p->curwidth = defaultwidth;
- p->curheight = (int) ((double) p->curwidth / GetAspRatio());
- }
-
- p->width = round (p->curwidth * mag);
- p->height = round (p->curheight * mag);
- p->curx = -x0;
- p->cury = -y0;
- }
-
- void GView::SetFrame(GraphFrame* f)
- /* so we can pick on the one upstairs */
- {
- frame = f;
- }
-
- Canvas *GView::GetCanvas()
- {
- return canvas;
- }
-
- void GView::UpdateP ()
- {
- okaytodraw = true; /* you asked for it */
- Interactor::Update();
- }
-
- void GView::UpdatePerspective ()
- {
- if (okaytodraw)
- {
- perspective->Update();
- }
- }
-
- void GView::UpdateG()
- {
- okaytodraw = true; /* you asked for it */
- Update();
- }
-
- void GView::Update()
- {
- UpdatePerspective();
- firsttime = true; /* if we've called it, we don't want to call DoGraph */
- Draw();
- }
-
- /**
- Draw and Redraw: We only want to do this if it's okay to draw and the
- canvas has been mapped i.e. != nil.
-
- Draw and Redraw are called from many places, including after a resize is
- done, so if we set a flag there, we know here that we have to call
- DoGraph. On each window resize, the Resize procedure seems to be called
- many times, so we don't want to call DoGraph from there.
-
- There's no intelligent way to redraw just part of the screen, so just
- do as Draw does.
-
- firsttime is sort of a misnomer. It distinguishes between calls to
- Draw by Update i.e. by UpdateG i.e. by the user and calls by mysterious
- parts of the InterViews software.
- **/
- void GView::Draw ()
- {
- if (okaytodraw && canvas != nil)
- {
- output->ClearRect(canvas, 0, 0, xmax, ymax);
-
- if (firsttime || !resizedone)
- {
- firsttime = false;
- resizedone = false;
- graph->DrawAll();
- }
- else
- {
- resizedone = false;
- DoGraph();
- }
- }
- }
-
- void GView::Redraw (Coord a, Coord b, Coord c, Coord d)
- {
- if (okaytodraw && canvas != nil)
- {
- output->ClearRect(canvas, a, b, c, d);
-
- if (firsttime || !resizedone)
- {
- firsttime = false;
- resizedone = false;
- graph->DrawAll();
- }
- else
- {
- resizedone = false;
- DoGraph();
- }
- }
- }
-
- void GView::Erase()
- {
- okaytodraw = false;
- /**
- inhibit redrawing the graph. After an erase, DoGraph will be called,
- which will redraw the graph. Between that time, any extra calls
- to draw the graph are pointless
- **/
- graph->DeleteAll();
- }
-
- void GView::CenterGraph ()
- {
- Perspective p;
-
- okaytodraw = false;
- /**
- after we center, UpdateG is eventually called. Any intervening
- calls to draw the graph are pointless
- **/
-
- /* get the proper perspective */
- p = *GetPerspective();
- mag = 1.0;
- x0 = 0;
- y0 = 0;
- FixPerspective(&p);
- Adjust(p);
-
- /* Adjust changes the following values, so reset them */
- mag = 1.0;
- x0 = 0;
- y0 = 0;
- }
-
- void GView::FocusGraph(int x, int y, float m)
- /**
- Magnify the graph by m, and translate it so (x, y) is the center
- **/
- {
- Perspective p;
-
- okaytodraw = false;
- /**
- after we focus, DoGraph is called. Any intervening
- calls to draw the graph are pointless
- **/
-
- p = *GetPerspective();
- p.curwidth = round(p.curwidth/m);
- p.curheight = round(p.curheight/m);
- Adjust(p);
-
- p = *GetPerspective();
- p.curx = round(m * x - canvas->Width()/2);
- p.cury = round(m * y - canvas->Height()/2);
- Adjust(p);
-
- okaytodraw = true;
- DoGraph();
- }
-
- void GView::ChangePGrad(int newx, int newy)
- {
- Perspective *p = GetPerspective();
-
- p->sx = newx;
- p->sy = newy;
- p->lx = 2 * newx;
- p->ly = 2 * newy;
- }
-
- void GView::GetBounds(int* bx, int* by, int* bwidth, int* bheight,
- int* bcurx, int* bcury, int* bcurwidth, int* bcurheight)
- {
- Perspective* p = GetPerspective();
-
- *bx = p->x0;
- *by = p->y0;
- *bwidth = p->width;
- *bheight = p->height;
- *bcurx = p->curx;
- *bcury = p->cury;
- *bcurwidth = p->curwidth;
- *bcurheight = p->curheight;
- }
-
- void GView::Resize()
- /**
- Undo the damage done by the Interactor Resize routine, which resets the
- pan gradients. Also, set the flag to indicate a resize was done.
- Eventually the top-level Resize routine calls Draw, and we'll know
- then to erase the graph and start over as opposed to just using
- the graph we have now
- **/
- {
- Perspective q = *GetPerspective();
-
- Interactor::Resize();
- Perspective *p = GetPerspective();
- FixPerspective(p);
- p->sx = q.sx;
- p->lx = q.lx;
- p->sy = q.sy;
- p->ly = q.ly;
-
- Perspective np = *p;
-
- if (q != np)
- {
- /* something's changed */
- resizedone = true;
- }
- }
-
- void GView::SetFHandler(FontHandler* f)
- {
- graph->SetFHandler(f);
- }
-
- static const slack = 3;
- /* for those of us who can't pinpoint a pixel with the mouse */
-
- void GView::ResetCurrent()
- /**
- There's a good chance the list of items under the cursor has
- changed, so rebuild it
- **/
- {
- if (current != nil)
- {
- delete[currno] current;
- current = nil;
- currno = 0;
- }
-
- FindIntersect();
- }
-
- /**
- In an effort to allow all the functionality of all 3 modes at once,
- you can hold down keys while pressing a mouse button to change the current
- mode. Originally, the shift key indicated change mode, the meta key
- indicated browse mode, and the control key indicated edit mode.
-
- Unfortunately, some window managers use these keys themselves, and
- such events never get to us. To further refine the idea, I decided to use
- surrounding keys in the same way. So:
- z, Z, ?, /, shift -- change mode
- space bar, meta -- browse mode
- a, A, ", ', control -- edit mode
- (equal treatment for lefties, if you notice).
-
- Unfortunately, whoever wrote InterViews didn't support
- KeyRelease events, so I can't tell when the normal keys are released.
- Their reason is that some keyboards don't notify the system of KeyRelease
- events. With that kind of reasoning, I'm surprised the thing supports
- colors.
-
- Anyway, the results are predictable: press Z, and you'll be in change
- mode until you press another key. And so on.
- **/
-
- static char* oldkey = "\0";
-
- boolean GView::BKeyPressed(Event& e)
- {
- return (e.meta || !strcmp(oldkey, " "));
- }
-
- boolean GView::CKeyPressed(Event& e)
- {
- return (e.shift || !strcmp(oldkey, "/") ||
- !strcmp(oldkey, "z") || !strcmp(oldkey, "?") ||
- !strcmp(oldkey, "Z"));
- }
-
- boolean GView::EKeyPressed(Event& e)
- {
- return (e.control || !strcmp(oldkey, "\'") ||
- !strcmp(oldkey, "a") || !strcmp(oldkey, "\"") ||
- !strcmp(oldkey, "A"));
- }
-
- void GView::Handle (Event& e)
- {
- ModeType buttonMode;
-
- if (e.eventType == UpEvent || e.eventType == DownEvent ||
- e.eventType == MotionEvent || e.eventType == KeyEvent &&
- e.target == this)
- {
- cursorx = e.x;
- cursory = e.y;
- ResetCurrent();
-
- if (e.eventType == MotionEvent)
- {
- DoMouseMove(e);
- }
- else
- {
- /**
- note what happens in the following when more than one "key"
- is pressed: The mode first in the if statement will win.
- I figured change mode was the most innocuous, and edit mode
- the most deadly, and wishing to avoid drastic inadvertent
- changes, I put change mode first and edit mode last
- **/
- if (CKeyPressed(e) ||
- (GetCModeFlag() && !(EKeyPressed(e) || BKeyPressed(e))))
- {
- buttonMode = cmode;
- }
- else if (BKeyPressed(e) ||
- (GetBModeFlag() && !EKeyPressed(e)))
- {
- buttonMode = bmode;
- }
- else
- {
- buttonMode = emode;
- }
-
- switch (e.eventType)
- {
- case UpEvent:
- switch (e.button)
- {
- case RIGHTMOUSE:
- DoRightUp(e, buttonMode);
- break;
- case MIDDLEMOUSE:
- DoMiddleUp(e, buttonMode);
- break;
- case LEFTMOUSE:
- DoLeftUp(e, buttonMode);
- break;
- }
- break;
- case DownEvent:
- switch (e.button)
- {
- case RIGHTMOUSE:
- DoRightDown(e, buttonMode);
- break;
- case MIDDLEMOUSE:
- DoMiddleDown(e, buttonMode);
- break;
- case LEFTMOUSE:
- DoLeftDown(e, buttonMode);
- break;
- }
- break;
- case KeyEvent:
- delete oldkey;
- oldkey = new char[e.len];
- strcpy (oldkey, e.keystring);
- break;
- default:
- break;
- }
- }
- }
- }
-
- void GView::FindIntersect()
- /**
- find which graphics objects are under the cursor. Make the cursor
- into a small box instead of a point so we don't have to be
- Joe Dexterity (Jane Dexterity?)
- **/
- {
- Coord tx1, tx2, ty1, ty2;
-
- if (currno == 0)
- {
- tx1 = cursorx + slack;
- tx2 = cursorx - slack;
- ty1 = cursory + slack;
- ty2 = cursory - slack;
- BoxObj b = BoxObj(tx1, ty1, tx2, ty2);
- currno = graph->myGraphicsIntersecting(b, current);
- }
- }
-
- void GView::translate (Coord x, Coord y, Coord* tx, Coord* ty)
- /* this isn't ever called, anyway */
- {
- int sx0, sy0, swidth, sheight;
-
- Perspective *p = GetPerspective();
- GetSBounds(&sx0, &sy0, &swidth, &sheight);
-
- /* Change to (0,1) bounding box */
- float ix = (float) (x + p->curx) / (float) p->width;
- float iy = (float) (y + p->cury) / (float) p->height;
- /* Change to screen coordinate system */
- *tx = round(ix * swidth - sx0);
- *ty = round(iy * sheight - sy0);
- }
-
-
- void GView::invtranslate (Coord x, Coord y, Coord* tx, Coord* ty)
- /* neither is this */
- {
- int sx0, sy0, swidth, sheight;
-
- Perspective *p = GetPerspective();
- GetSBounds(&sx0, &sy0, &swidth, &sheight);
-
- /* Change to (0,1) bounding box */
- float ix = (float) (x + sx0) / (float) swidth;
- float iy = (float) (y + sy0) / (float) sheight;
- /* Change to screen coordinate system */
- *tx = round(ix * p->width - p->curx);
- *ty = round(iy * p->height - p->cury);
- }
-
- boolean GView::CurOnNonDummyNode()
- /* are we on a non-dummy node? */
- {
- int i;
-
- FindIntersect();
- for (i = 0; i < currno; i++)
- {
- if (current[i]->gdtype == node)
- {
- return true;
- }
- }
-
- return false;
- }
-
- boolean GView::CurOnNode()
- /* are we on a node */
- {
- int i;
-
- FindIntersect();
- for (i = 0; i < currno; i++)
- {
- if ((current[i]->gdtype == node) || (current[i]->gdtype == dummy))
- {
- return true;
- }
- }
-
- return false;
- }
-
- boolean GView::CurOnEdge()
- /* are we on an edge? */
- {
- int i;
-
- FindIntersect();
- for (i = 0; i < currno; i++)
- {
- if (current[i]->gdtype == edge)
- {
- return true;
- }
- }
-
- return false;
- }
-
- char *GView::CurNode()
- /**
- what node are we on?
- Note: there should be a CurNonDummyNode. Where is it?
- **/
- {
- int i;
-
- for (i = 0; i < currno; i++)
- {
- if ((current[i]->gdtype == node) || (current[i]->gdtype == dummy))
- {
- return current[i]->gpart;
- }
- }
-
- return nil;
- }
-
- char *GView::CurEdge()
- /* what edge are we on? */
- {
- int i;
-
- for (i = 0; i < currno; i++)
- {
- if (current[i]->gdtype == edge)
- {
- return current[i]->gpart;
- }
- }
-
- return nil;
- }
-
- static Coord wid, ht;
-
- void GView::SetupMove(char *mnode, char* prev[], char* next[], int numprev,
- int numnext)
- /* we're about to do a move */
- {
- graph->SetupMove(mnode, prev, next, numprev, numnext,
- &wid, &ht);
- }
-
- void GView::DoMove(Event& e)
- /**
- Presumably, SetupMove has already been called. Create a fake
- node so the user can see it as it slides around. When the button's
- released, call DoMoveEnd
-
- The sliding rectangle code is inspired by the InterViews source
- for sliders
- **/
- {
- SlidingRect* r;
-
- DoSetUpMove();
- Listen(allEvents);
- r = new SlidingRect(output, canvas, e.x - wid, e.y - ht,
- e.x + wid, e.y + ht, e.x, e.y);
- r->Draw();
-
- do {
- switch (e.eventType)
- {
- case MotionEvent:
- e.target->GetRelative(e.x, e.y, this);
- Constrain(e);
- r->Track(e.x, e.y);
- break;
- default:
- break;
- }
-
- Read(e);
- } while (e.eventType != UpEvent || e.button != MIDDLEMOUSE);
-
- Constrain(e); /* don't let the rectangle go off the screen */
- r->Erase();
- cursorx = e.x;
- cursory = e.y;
- Listen(input);
- delete r;
- DoMoveEnd();
- }
-
- void GView::DoInsArc(Event& e)
- /**
- Inserting arcs is a little trickier, since you can't finish
- with the arc just anywhere. Assuming the starting node is
- viable, keep inserting arcs until DoInsArcEnd says to stop
- **/
- {
- RubberLine* r;
-
- if (DoSetUpInsArc()) /* check the start node is okay */
- {
- do
- {
- Listen(allEvents);
- r = new RubberLine(output, canvas, e.x, e.y, e.x, e.y);
-
- do
- {
- switch (e.eventType)
- {
- case MotionEvent:
- e.target->GetRelative(e.x, e.y, this);
- Constrain(e);
- r->Track(e.x, e.y);
- break;
- default:
- break;
- }
-
- Read(e);
- } while (e.eventType != UpEvent || e.button != LEFTMOUSE);
-
- Constrain(e); /* don't let the endpoint go off the screen */
- r->Erase();
- cursorx = e.x;
- cursory = e.y;
- ResetCurrent();
- /* need to know where we've ended up, so recalculate current */
- Listen(input);
- delete r;
- } while (!DoInsArcEnd());
- }
- }
-
- void GView::Constrain(Event& e)
- /* don't go outside the bounds (0, 0), (xmax, ymax) */
- {
- e.x = min(max(e.x, 0), xmax);
- e.y = min(max(e.y, 0), ymax);
- }
-
- void GView::ChangeBC()
- {
- graph->ChangeBC();
- }
-
- void GView::ChangePA()
- {
- graph->ChangePA();
- }
-
- void GView::ChangeForceNL()
- {
- graph->ChangeForceNL();
- }
-
- void GView::ChangeForceEL()
- {
- graph->ChangeForceEL();
- }
-
- void GView::ChangeMarkDummy()
- {
- graph->ChangeMarkDummy();
- }
-
- void GView::ChangeCurNodeLabel(char* s)
- /* find the current node, then change it's label to s */
- {
- for (int i = 0; i < currno; i++)
- {
- if (current[i]->gdtype == node || current[i]->gdtype == dummy)
- {
- graph->ChangeNodeLabel(current[i]->gpart, s);
- break;
- }
- }
- }
-
- void GView::GetCurPos(int* px, int* py)
- /* return the current cursor position */
- {
- Perspective* p = GetPerspective();
-
- *px = cursorx;
- *py = cursory;
- }
-
- /**
- What follows are the routines to handle mouse button presses
- and releases. GetInCModeFlag, tell indicates if we're
- in a special mode. In this case, other operations are verboten
- **/
-
- void GView::DoLeftDown(Event& e, ModeType m)
- {
- if (GetInCModeFlag())
- {
- /* wait for button to be released */
- }
- else
- {
- switch (m)
- {
- case bmode:
- case cmode:
- break;
-
- case emode:
- if (CurOnNode())
- /* if on node, then insert arc */
- {
- if (CurOnNonDummyNode())
- {
- DoInsArc(e);
- ResetCurrent();
- ResetCursor();
- }
- else
- {
- frame->ChangeStatusLine(
- "Must start edge on a non-dummy node.");
- }
- }
- else
- {
- DoSInsertNode();
- ResetCurrent();
- }
- break;
- }
- }
- }
-
- void GView::DoLeftUp(Event& e, ModeType m)
- {
- if (GetInCTextModeFlag())
- {
- if (CurOnNode())
- {
- DoChangeNodeText();
- }
- else
- {
- frame->ChangeStatusLine(
- "change node text: cursor must be on a node");
- }
-
- ClearInModeFlag();
- }
- else if (GetInCEdgeLabelModeFlag())
- {
- if (CurOnEdge())
- {
- DoChangeEdgeText();
- }
- else
- {
- frame->ChangeStatusLine(
- "change edge label: cursor must be on an edge");
- }
-
- ClearInModeFlag();
- }
- else if (GetInFNodeModeFlag())
- {
- if (CurOnNode())
- {
- DoFocusCurNode();
- ResetCurrent();
- }
- else
- {
- frame->ChangeStatusLine(
- "focus node: cursor must be on a node");
- }
-
- ClearInModeFlag();
- }
- else
- {
- switch (m)
- {
- case emode:
- break;
-
- case bmode:
- if (CurOnNode())
- {
- if (CurOnNonDummyNode())
- {
- if (GetInEFlag() || GetOutEFlag())
- {
- DoSetEdges();
-
- if (GetInEFlag() && GetOutEFlag())
- {
- frame->ChangeStatusLine(
- "in and out edge attributes set");
- }
- else if (GetInEFlag())
- {
- frame->ChangeStatusLine(
- "in edge attributes set");
- }
- else
- {
- frame->ChangeStatusLine(
- "out edge attributes set");
- }
- }
- else
- {
- frame->ChangeStatusLine("No effect: change in/out edge flags both off");
- }
- }
- else
- {
- frame->ChangeStatusLine("set in/out edge attributes: must be on non-dummy node");
- }
- }
- else
- {
- frame->ChangeStatusLine(
- "set in/out edge attributes: cursor must be on a node");
- }
- break;
-
- case cmode:
- if (CurOnNode())
- {
- if (CurOnNonDummyNode())
- {
- DoSetNode();
- frame->ChangeStatusLine("node attributes set");
- }
- else
- {
- frame->ChangeStatusLine(
- "set node attributes: must be on non-dummy node");
- }
- }
- else if (CurOnEdge())
- {
- DoSetEdge();
- frame->ChangeStatusLine("edge attributes set");
- }
- else
- {
- frame->ChangeStatusLine(
- "set attributes: cursor must be on a node or an edge ");
- }
- }
- }
-
- ResetCursor();
- }
-
- void GView::DoMiddleDown(Event& e, ModeType m)
- {
- if (GetInCModeFlag())
- {
- /* ignore this button */
- }
- else
- {
- switch (m)
- {
- case bmode:
- case cmode:
- break;
-
- case emode:
- if (CurOnNode())
- {
- DoMove(e);
- ResetCurrent();
- ResetCursor();
- }
- else
- {
- frame->ChangeStatusLine(
- "Move must start on a node");
- }
- break;
- }
- }
- }
-
- void GView::DoMiddleUp(Event& e, ModeType m)
- {
- if (GetInCModeFlag())
- {
- /* ignore this button */
- }
- else
- {
- switch (m)
- {
- case emode:
- /* nothing to do */
- break;
-
- case bmode:
- if (CurOnNode())
- {
- if (CurOnNonDummyNode())
- {
- if (GetInEFlag() || GetOutEFlag())
- {
- DoResetEdges();
-
- if (GetInEFlag() && GetOutEFlag())
- {
- frame->ChangeStatusLine(
- "in and out edge attributes reset");
- }
- else if (GetInEFlag())
- {
- frame->ChangeStatusLine(
- "in edge attributes reset");
- }
- else
- {
- frame->ChangeStatusLine(
- "out edge attributes reset");
- }
- }
- else
- {
- frame->ChangeStatusLine("No effect: change in/out edge flags both off");
- }
- }
- else
- {
- frame->ChangeStatusLine("reset in/out edge attributes: must be on non-dummy node");
- }
- }
- else
- {
- frame->ChangeStatusLine("reset in/out edge attributes: cursor must be on a node");
- }
- break;
-
- case cmode:
- if (CurOnNode())
- {
- if (CurOnNonDummyNode())
- {
- DoResetNode();
- frame->ChangeStatusLine("node attributes reset");
- }
- else
- {
- frame->ChangeStatusLine(
- "reset node attributes: must be on non-dummy node");
- }
- }
- else if (CurOnEdge())
- {
- DoResetEdge();
- frame->ChangeStatusLine("edge attributes reset");
- }
- else
- {
- frame->ChangeStatusLine(
- "reset attributes: must be on a node or an edge");
- }
- }
- }
-
- ResetCursor();
- }
-
- void GView::DoRightDown(Event& e, ModeType m)
- {
- if (GetInCModeFlag())
- {
- /* ignore this button */
- }
- else
- {
- switch (m)
- {
- case bmode:
- case cmode:
- case emode:
- /* wait for button to be released */
- break;
- }
- }
- }
-
- void GView::DoRightUp(Event& e, ModeType m)
- {
- if (GetInCModeFlag())
- {
- /* ignore this button */
- }
- else
- {
- switch (m)
- {
- case emode:
- if (CurOnNode())
- {
- DoSDeleteNode();
- ResetCurrent();
- }
- else if (CurOnEdge())
- {
- DoSDeleteArc();
- ResetCurrent();
- }
- else
- {
- frame->ChangeStatusLine (
- "delete: cursor must be on a node or edge");
- }
- break;
-
- case bmode:
- if (CurOnNonDummyNode())
- {
- DoShowNAttr();
- }
- else if (CurOnEdge())
- {
- DoShowEAttr();
- }
- else
- {
- DoEraseAttrBox();
- }
- break;
-
- case cmode:
- if (CurOnNode())
- {
- if (CurOnNonDummyNode())
- {
- DoCycleNode();
- frame->ChangeStatusLine("node attributes changed");
- }
- else
- {
- frame->ChangeStatusLine("change node attributes: must be on non-dummy node");
- }
- }
- else if (CurOnEdge())
- {
- DoCycleEdge();
- frame->ChangeStatusLine("edge attributes changed");
- }
- else
- {
- frame->ChangeStatusLine(
- "change attributes: must be on a node or an edge ");
- }
- break;
- }
- }
-
- ResetCursor();
- }
-
- /**
- It used to be you could display the node label if you moved onto
- the node and an option was set. I found this very annoying. Besides,
- it didn't work.
- **/
- static char* oldnode = nil;
-
- void GView::DoMouseMove(Event& e)
- {
- char* curnode = nil;
-
- if (CurOnNode())
- {
- curnode = CurNode();
-
- if (curnode != oldnode)
- {
- // MoveOn();
- }
- }
-
- if (curnode != oldnode && oldnode != nil)
- {
- // MoveOff(oldnode);
- }
-
- oldnode = curnode;
- ResetCursor();
- }
-
- /**
- Set the cursor to its proper value
- **/
- void GView::ResetCursor()
- {
- if (CurOnNonDummyNode())
- {
- frame->SetCurs(nodeC);
- }
- else if (CurOnEdge())
- {
- frame->SetCurs(lineC);
- }
- else if (CurOnNode()) /* dummy node */
- {
- frame->SetCurs(nodeC);
- }
- else
- {
- frame->SetCurs(mainC);
- }
- }
-
- /**
- The following six routines are mostly stolen from the InterViews source
- for the GraphicBlock class
- **/
- void GView::Normalize (Perspective& np)
- {
- register Perspective* p = perspective;
- float hfactor, vfactor;
-
- if (p->width != np.width)
- {
- hfactor = float(p->width) / float(np.width);
- np.x0 = round(hfactor * float(np.x0));
- np.width = p->width;
- np.curx = round(hfactor * float(np.curx));
- np.curwidth = round(hfactor * float(np.curwidth));
- np.sx = round(hfactor * float(np.sx));
- }
-
- if (p->height != np.height)
- {
- vfactor = float(p->height) / float(np.height);
- np.y0 = round(vfactor * float(np.y0));
- np.height = p->height;
- np.cury = round(vfactor * float(np.cury));
- np.curheight = round(vfactor * float(np.curheight));
- np.sy = round(vfactor * float(np.sy));
- }
- }
-
- float GView::ScaleFactor (Perspective& np)
- {
- register Perspective* p = perspective;
- float factor = 1;
- Coord dx, dy;
-
- dx = abs(p->curwidth - np.curwidth);
- dy = abs(p->curheight - np.curheight);
-
- if (dx < dy)
- {
- factor = float(p->curwidth) / float(np.curwidth);
- }
- else
- {
- factor = float(p->curheight) / float(np.curheight);
- }
-
- return factor;
- }
-
- void GView::Zoom (Perspective& np)
- {
- register Perspective* p = perspective;
- Coord cx, cy, halfw, halfh, dx, dy;
- float factor = ScaleFactor(np);
-
- if (factor != 1.0)
- {
- cx = np.curx + np.curwidth/2;
- cy = np.cury + np.curheight/2;
- halfw = p->curwidth/2;
- halfh = p->curheight/2;
- dx = (p->curx + halfw) - cx;
- dy = (p->cury + halfh) - cy;
-
- x0 = round((x0 + dx - halfw)*factor + halfw);
- y0 = round((y0 + dy - halfh)*factor + halfh);
- p->width = round(p->width * factor);
- p->height = round(p->height * factor);
- p->curx = round(float(cx) * factor) - halfw;
- p->cury = round(float(cy) * factor) - halfh;
- mag *= factor;
- }
- }
-
- void GView::Scroll (Perspective& np)
- {
- register Perspective* p = perspective;
- Coord dx, dy;
-
- dx = p->curx - np.curx;
- dy = p->cury - np.cury;
- x0 += dx;
- y0 += dy;
- p->curx = np.curx;
- p->cury = np.cury;
- }
-
- void GView::Adjust (Perspective& np)
- {
- register Perspective* p = perspective;
- Perspective ptmp;
-
- if (*p != np)
- {
- if (okaytodraw)
- {
- frame->ChangeStatusLine("Adjusting view of graph", true);
- }
-
- Normalize(np);
- ptmp = *p;
-
- if (np.curwidth != canvas->Width() || np.curheight!=canvas->Height())
- {
- Zoom(np);
- }
- else
- {
- Scroll(np);
- }
-
- if (okaytodraw)
- {
- p->Update();
-
- if (ptmp != *p)
- {
- DoGraph();
- }
- }
- }
- }
-
- void GView::SetMagnification (float m)
- {
- register Perspective* p = perspective;
- float factor;
- Coord cx, cy, halfw, halfh;
-
- factor = m/mag;
-
- if (factor != 1.0)
- {
- halfw = p->curwidth/2;
- halfh = p->curheight/2;
- cx = p->curx + halfw;
- cy = p->cury + halfh;
-
- x0 = round((x0 - halfw)*factor + halfw);
- y0 = round((y0 - halfh)*factor + halfh);
- p->width = round(p->width * factor);
- p->height = round(p->height * factor);
- p->curx = round(float(cx) * factor) - halfw;
- p->cury = round(float(cy) * factor) - halfh;
-
- mag = m;
-
- if (okaytodraw)
- {
- p->Update();
- DoGraph();
- }
- }
- }
-